package esi.t2no1.ws.impl;

import static esi.t2no1.util.WsUtil.createSoapFault;
import static esi.t2no1.ws.Ws.NETWORK_OWNER_ID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import com.supplier.ClientTerminationMessage;
import com.supplier.SupplierChangeReplyMessage;

import ee.ut.cs.esi.t2es2.services.Aperak;
import ee.ut.cs.esi.t2es2.services.ClientContractMessage;
import esi.t2no1.dao.contract.ContractDAO;
import esi.t2no1.dao.contract.ContractDTO;
import esi.t2no1.dao.meterdata.MeterDataDAO;
import esi.t2no1.dao.meterdata.MeterDataDTO;
import esi.t2no1.ws.Ws;
import esi.t2no1.ws.model.ContractOutput;
import esi.t2no1.ws.model.CreateContractInput;
import esi.t2no1.ws.model.CreateContractOutput;
import esi.t2no1.ws.service.ClientService;

/**
 * For the description of the class, check the implemented interface.
 * 
 * @author Kaarel Jõgeva
 */
public class ClientServiceImpl implements ClientService {

	private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(ClientServiceImpl.class);

	private ContractDAO contractDAO;
	private MeterDataDAO meterDataDAO;
	private int contractSequence = 0;

	@Override
	public CreateContractOutput createContract(final CreateContractInput input) {

		CreateContractOutput output = new CreateContractOutput();
		if (!validateContractInput(input)) {
			// Send negative Z04 to new supplier
			if ("T2ES1".equalsIgnoreCase(input.getNewSupplierId())) {
				new Thread(new Runnable() {
					@Override
					public void run() {
						SupplierChangeReplyMessage message = new SupplierChangeReplyMessage();
						message.setAccepted(false);
						message.setAreaId(Integer.parseInt(input.getAreaId()));
						message.setFacilityId(Integer.parseInt(input
								.getFacilityId()));
						message.setNOId(NETWORK_OWNER_ID);
						Ws.T2ES1.supplierChangeReply(message);
					}
				}).start();
			} else if ("T2ES2".equalsIgnoreCase(input.getNewSupplierId())) {
				new Thread(new Runnable() {
					@Override
					public void run() {
						Ws.T2ES2.submitAPERAK(new Aperak());
					}
				}).start();
			}
			return output;
		}

		// Send positive Z04 to new supplier
		if ("T2ES1".equalsIgnoreCase(input.getNewSupplierId())) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					SupplierChangeReplyMessage message = new SupplierChangeReplyMessage();
					message.setAccepted(true);
					message.setAreaId(Integer.parseInt(input.getAreaId()));
					message.setFacilityId(Integer.parseInt(input
							.getFacilityId()));
					message.setNOId(NETWORK_OWNER_ID);
					Ws.T2ES1.supplierChangeReply(message);
				}
			}).start();
		} else {
			new Thread(new Runnable() {
				@Override
				public void run() {
					Ws.T2ES2.submitAPERAK(new Aperak());
				}
			}).start();
		}

		// Save the contract details if everything is okay.
		ContractDTO contractDTO = new ContractDTO(input);
		String contractNumber = Ws.NETWORK_OWNER_ID + "/"
				+ input.getNewSupplierId() + "/" + System.currentTimeMillis();
		contractDTO.setClientContractNumber(contractNumber);
		contractDAO.createContract(contractDTO);

		// Notify the old supplier
		if ("T2ES1".equalsIgnoreCase(input.getOldSupplierId())) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					ClientTerminationMessage message = new ClientTerminationMessage();
					message.setAreaId(Integer.parseInt(input.getAreaId()));
					message.setFacilityId(Integer.parseInt(input
							.getFacilityId()));
					message.setNOId(NETWORK_OWNER_ID);
					// TODO What format is needed?
					message.setEndDate(input.getStartDate().toGMTString());
					Ws.T2ES1.terminateContract(message);
				}
			}).start();
		} else if ("T2ES2".equalsIgnoreCase(input.getOldSupplierId())) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					Ws.T2ES2.submitEndContract(new ClientContractMessage());
				}
			}).start();
		}

		// Send preliminary load shares
		// Note: T2ES1 and T2ES2 have no separate service endpoints for
		// "preliminary load shares", sending only metering data.

		// Send metering data (MSCONS), if info available
		MeterDataDTO meterDataDTO = meterDataDAO.getLatestMeterData(
				input.getAreaId(), input.getFacilityId());
		if (meterDataDTO != null) {
			if ("T2ES1".equalsIgnoreCase(input.getNewSupplierId())
					|| "T2ES1".equalsIgnoreCase(input.getOldSupplierId())) {
				Ws.T2ES1.sendMeteringReadings(meterDataDTO.toES1MeterData());
			}
			if ("T2ES2".equalsIgnoreCase(input.getNewSupplierId())
					|| "T2ES2".equalsIgnoreCase(input.getOldSupplierId())) {
				try {
					Ws.T2ES2.submitMeterData(meterDataDTO.toES2MeterData());
				} catch (Exception e) {
					LOG.error("Failed to parse metering data to T2ES2 message format");
				}
			}
		}

		return new CreateContractOutput(contractNumber);
	}

	private boolean validateContractInput(CreateContractInput input) {
		// 0. Reject every second contract
		if (++contractSequence % 2 == 0) {
			return false;
		}

		if (contractDAO.getContractByDate(input.getAreaId(),
				input.getFacilityId(), input.getStartDate()) != 0) {
			return false;
		}

		return true;
	}

	@Override
	public ContractOutput getContract(String contractNumber) {
		if (!StringUtils.hasText(contractNumber)) {
			throw createSoapFault("Contract number cannot be empty!",
					"emptyContractNumber");
		}

		ContractOutput contractOutput = contractDAO
				.getContractOutput(contractNumber);

		if (contractOutput == null) {
			throw createSoapFault("Contract with given contractNumber ("
					+ contractNumber + ") cannot be found!", "contractNotFound");
		}

		return contractOutput;
	}

	@Autowired
	public void setContractDAO(ContractDAO contractDAO) {
		this.contractDAO = contractDAO;
	}

	@Autowired
	public void setMeterDataDAO(MeterDataDAO meterDataDAO) {
		this.meterDataDAO = meterDataDAO;
	}
}